#pragma once
#include "stdafx.h"
#include "ITime.h"
#include <memory>
#include <ctime>
#include <sstream>

#include <iostream>

/*
FIXME: not threadsafe. gmtime uses a single struct!
*/
class Time : public ITime {
private:
	int year;
	int month;
	int day;
	int hour;
	int minute;
	int second;

	void init(int year, int month=1, int day=1, int hour=0, int minute=0, int second=0) {
		// FIXME: make sure inputs are valid
		this->year = year;
		this->month = month;
		this->day = day;
		this->hour = hour;
		this->minute = minute;
		this->second = second;
	}

public:
	/*
	Initializes to current time in UTC.
	*/
	Time() {
		std::time_t now = std::time(nullptr); // Get current time in UNIX epoch
		struct tm* utcNow = std::gmtime(&now); // Convert to UTC
		init(
			utcNow->tm_year + 1900,
			utcNow->tm_mon + 1,
			utcNow->tm_mday,
			utcNow->tm_hour,
			utcNow->tm_min,
			utcNow->tm_sec
			);
	}

	Time(int year, int month=1, int day=1, int hour=0, int minute=0, int second=0) {
		init(year, month, day, hour, minute, second);
	}

	/*
	Initializes to specified Unix time specifeid in UTC
	*/
	Time(long int unixTime) {
		const time_t t = unixTime;
		//struct tm* utcNow = std::gmtime(&t); // Convert to UTC
		struct tm* utcNow = std::localtime(&t);
		init(
			utcNow->tm_year + 1900,
			utcNow->tm_mon + 1,
			utcNow->tm_mday,
			utcNow->tm_hour,
			utcNow->tm_min,
			utcNow->tm_sec
			);
	}

	Time(const Time& other) {
		this->year = other.getYear();
		this->month = other.getMonth();
		this->day = other.getDay();
		this->hour = other.getHour();
		this->minute = other.getMinute();
		this->second = other.getSecond();
	}

	virtual ~Time() {}

	static Time today() {
		Time time;
		time.hour = 0;
		time.minute = 0;
		time.second = 0;
		return time;
	}

	virtual std::unique_ptr<ITime> copy() const {
		return std::unique_ptr<ITime>(new Time(*this));
	}

	const long int getUnixTime() const {
		struct tm timeStruct;
		timeStruct.tm_year = getYear() - 1900;
		timeStruct.tm_mon = getMonth() - 1;
		timeStruct.tm_mday = getDay();
		timeStruct.tm_hour = getHour();
		timeStruct.tm_min = getMinute();
		timeStruct.tm_sec = getSecond();
		timeStruct.tm_isdst = -1;

		return std::mktime(&timeStruct);
	}

	virtual const long int getDifferenceInSeconds(const ITime& other) const {
		struct tm timeStruct;
		timeStruct.tm_year = other.getYear() - 1900;
		timeStruct.tm_mon = other.getMonth() - 1;
		timeStruct.tm_mday = other.getDay();
		timeStruct.tm_hour = other.getHour();
		timeStruct.tm_min = other.getMinute();
		timeStruct.tm_sec = other.getSecond();
		timeStruct.tm_isdst = -1; // Make mktime determine whether daylight savings time in effect

		std::time_t otherSeconds = std::mktime(&timeStruct);

		return (long) std::difftime(getUnixTime(), otherSeconds);
	}

	virtual const int getYear() const { return year; }
	virtual const int getMonth() const { return month; }
	virtual const int getDay() const { return day; }
	virtual const int getHour() const { return hour; }
	virtual const int getMinute() const { return minute; }
	virtual const int getSecond() const { return second; }

	virtual const std::string getRepresentation() const {
		std::ostringstream string;
		string << "Time[year=" << year << " month=" << month << " day=" << day << " hour=" << hour
			<< " minute=" << minute << " second=" << second << "]";
		return string.str();
	}

};